#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "driver/ledc.h"
#include "esp_err.h"

#define LEDC_LS_TIMER          LEDC_TIMER_1
#define LEDC_LS_MODE           LEDC_LOW_SPEED_MODE
#define LEDC_LS_CH0_GPIO       (6)
#define LEDC_LS_CH0_CHANNEL    LEDC_CHANNEL_0

#define FREQUENCY_PWM          4000
/*
 * This callback function will be called when fade operation has ended
 * Use callback only if you are aware it is being called inside an ISR
 * Otherwise, you can use a semaphore to unblock tasks
 */

#define USE_PWM_FADING         0

ledc_channel_config_t ledc_channel;
SemaphoreHandle_t counting_sem;

#if USE_PWM_FADING
static IRAM_ATTR bool cb_ledc_fade_end_event(const ledc_cb_param_t *param, void *user_arg)
{
    BaseType_t taskAwoken = pdFALSE;

    if (param->event == LEDC_FADE_END_EVT) {
        SemaphoreHandle_t counting_sem = (SemaphoreHandle_t) user_arg;
        xSemaphoreGiveFromISR(counting_sem, &taskAwoken);
    }

    return (taskAwoken == pdTRUE);
}
#endif
void led_pwm_init(void)
{
    /*
     * Prepare and set configuration of timers
     * that will be used by LED Controller
     */
    ledc_timer_config_t ledc_timer = {
        .duty_resolution = LEDC_TIMER_13_BIT, // resolution of PWM duty
        .freq_hz = FREQUENCY_PWM,                      // frequency of PWM signal
        .speed_mode = LEDC_LS_MODE,           // timer mode
        .timer_num = LEDC_LS_TIMER,            // timer index
        .clk_cfg = LEDC_AUTO_CLK,              // Auto select the source clock
    };
    // Set configuration of timer0 for high speed channels
    ledc_timer_config(&ledc_timer);
    /*
     * Prepare individual configuration
     * for each channel of LED Controller
     * by selecting:
     * - controller's channel number
     * - output duty cycle, set initially to 0
     * - GPIO number where LED is connected to
     * - speed mode, either high or low
     * - timer servicing selected channel
     *   Note: if different channels use one timer,
     *         then frequency and bit_num of these channels
     *         will be the same
     */
    ledc_channel.channel    = LEDC_LS_CH0_CHANNEL;
    ledc_channel.duty       = 0;
    ledc_channel.gpio_num   = LEDC_LS_CH0_GPIO;
    ledc_channel.speed_mode = LEDC_LS_MODE;
    ledc_channel.hpoint     = 0;
    ledc_channel.timer_sel  = LEDC_LS_TIMER;
    ledc_channel.flags.output_invert = 0;
    // Set LED Controller with previously prepared configuration
    ledc_channel_config(&ledc_channel);

    // Initialize fade service.
    // 节省内存，暂时不使用
    #if USE_PWM_FADING
    ledc_fade_func_install(0);
    ledc_cbs_t callbacks = {
        .fade_cb = cb_ledc_fade_end_event
    };
    counting_sem = xSemaphoreCreateCounting(1, 0);

    ledc_cb_register(ledc_channel.speed_mode, ledc_channel.channel, &callbacks, (void *) counting_sem);
    #endif

    
    ledc_set_duty(ledc_channel.speed_mode, ledc_channel.channel, 4000);
    ledc_update_duty(ledc_channel.speed_mode, ledc_channel.channel);

}

void led_pwm_set(int brightness)
{
    if(brightness>100 || brightness<0)
        return;
    int value = 40*brightness;
    ledc_set_duty(ledc_channel.speed_mode, ledc_channel.channel, value);
    ledc_update_duty(ledc_channel.speed_mode, ledc_channel.channel);
}

#if USE_PWM_FADING
void led_pwm_set_gradient(int brightness)
{
    static uint8_t light=0;
    if(brightness > 100 || brightness < 0 || light == brightness)
        return;
    int value = 40*brightness;
    ledc_set_fade_with_time(ledc_channel.speed_mode,
                            ledc_channel.channel, value, abs(light-brightness)*5);
    ledc_fade_start(ledc_channel.speed_mode,
                        ledc_channel.channel, LEDC_FADE_NO_WAIT);

    light = brightness;

    xSemaphoreTake(counting_sem, portMAX_DELAY);
}
#endif
